/*
	Visp is an opensource visual performance application built with Adobe® AIR™.
	Copyright (C) 2007 Mike Creighton
	
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
	
	Mike Creighton can be found at http://www.mikecreighton.com		
*/
package
{
	import flash.display.Sprite;
	import mx.core.ClassFactory;
	import flash.display.MovieClip;
	import mx.effects.AnimateProperty;
	import mx.effects.easing.Quintic;
	import flash.events.Event;
	import mx.events.TweenEvent;
	import com.visp.events.LifecycleEvent;

	[Event(name="destroyed", type="com.visp.events.LifecycleEvent")]
	[Event(name="birthed", type="com.visp.events.LifecycleEvent")]
	
	/**
	 * Construct is a grouping of Structures (black rectangular shapes) that
	 * expands (using explode() function) and contracts.
	 */
	public class Construct extends Sprite
	{	
		public var isDestroying : Boolean;
		
		private var _birthed : Array;
		private var _rotSpeeds : Array;
		private var _deathCount : int;
		private var _rotMultiplier : Number;
		
		private static const MAX_STRUCTS : int = 10;
		private static const MIN_STRUCTS : int = 3;
		
		public function Construct()
		{
			super();
			this.isDestroying = false;			
			this.dispatchEvent(new LifecycleEvent(LifecycleEvent.BIRTHED));
		}
				
		/**
		 * Creates a random number of Structure Sprites and tells them to 
		 * grow (yScale).
		 */
		public function explode(ratio : Number, rotationSpeed : Number) : void
		{
			// Come up with a random number of structures to explode with (based on pressure ratio).
			var num : int = MIN_STRUCTS + Math.round(ratio * (MAX_STRUCTS - MIN_STRUCTS));
			var index : int;
			var struct : Structure;
			var ani : AnimateProperty;
			var dur : int = 200 + (1 - ratio) * Math.floor(Math.random() * 300);
			
			this._rotMultiplier = rotationSpeed;
			this._birthed = new Array();
			this._rotSpeeds = new Array();
			
			while(num --){
				struct = new Structure();
				
				struct.rotation = Math.random() * 360;
				this._rotSpeeds.push(Math.random() * 2);
				
				this.addChild(struct);
				this._birthed.push(struct);
				
				ani = new AnimateProperty(struct);
				ani.property = "scaleY";
				ani.fromValue = 0;
				ani.toValue = 0.1 + ratio * 0.9;
				ani.easingFunction = Quintic.easeOut;
				ani.duration = dur;
				ani.play();
				
				struct.scaleX = ani.toValue;
			}
			this._deathCount = 0;
			
			this.addEventListener(Event.ENTER_FRAME, this._enterFrame);
		}
		
		/**
		 * EnterFrame callback used to animate the rotation of
		 * the structures in this Construct.
		 */
		private function _enterFrame(e : Event) : void
		{
			var i : int = this._birthed.length;
			var struct : Structure;
			while(i--){
				struct = this._birthed[i] as Structure;
				struct.rotation += this._rotSpeeds[i] * this._rotMultiplier;
			}
		}
		
		public function updateRotationSpeed(ratio : Number) : void
		{
			this._rotMultiplier = ratio;
		}
		
		public function destroy() : void
		{
			this.isDestroying = true;
			var i : int = this._birthed.length;
			var struct : Structure;
			var ani : AnimateProperty;
			var al : AnimateProperty;
			var dur : int = 200 + Math.floor(Math.random() * 300);
			
			while(i--){
				struct = this._birthed[i] as Structure;
				
				ani = new AnimateProperty(struct);
				ani.addEventListener(TweenEvent.TWEEN_END, this._handleStructDied);
				ani.property = "scaleY";
				ani.fromValue = struct.scaleY;
				ani.toValue = 0;
				ani.easingFunction = Quintic.easeIn;
				ani.duration = dur;
				ani.play();
				
				al = new AnimateProperty(struct);
				al.property = "alpha";
				al.fromValue = struct.alpha;
				al.toValue = 0;
				al.easingFunction = Quintic.easeIn;
				al.duration = dur;
				
				al.play();
			}
		}
		
		private function _handleStructDied(e : TweenEvent) : void
		{
			var ani : AnimateProperty = e.target as AnimateProperty;
			ani.removeEventListener(TweenEvent.TWEEN_END, this._handleStructDied);
			this._deathCount++;
			this._checkIfDead();
		}
		
		private function _checkIfDead() : void
		{
			if(this._deathCount == this._birthed.length){
				this.removeEventListener(Event.ENTER_FRAME, this._enterFrame);
				this.dispatchEvent(new LifecycleEvent(LifecycleEvent.DESTROYED));
			}
		}
	}
}

/**
 * Structure is a special black rectangle used as our graphical element
 * that makes up the Construct.
 */
import flash.display.Sprite;

class Structure extends Sprite
{
	public function Structure()
	{
		this.graphics.beginFill(0x000000, 1);
		var randWidth : Number = 50 + Math.round(Math.random() * 150);
		var randHeight : Number = 100 + Math.round(Math.random() * 400);
		this.graphics.drawRect(-1 * randWidth / 2, -1 * randHeight, randWidth, randHeight);
		this.graphics.endFill();
	}
}